﻿#pragma once

#include <fstream>
#include <map>
#include <bit>
#include <cassert>
#include <memory>
#include <vector>

#include "EndianConverter.h"

#ifdef __GNUC__
#include <string.h>
class StringConvert
{
public:
    static std::string ws2s(const std::wstring& wstr)
    {
        std::string locale(::setlocale(LC_ALL, ""));
        auto src = wstr.c_str();
        auto destSize = ::wcstombs(nullptr, src, 0) + 1;
        auto dest = new char[destSize];
        ::memset(dest, 0, destSize);
        ::wcstombs(dest, src, destSize);
        std::string result(dest);
        delete[] dest;
        ::setlocale(LC_ALL, locale.c_str());
        return result;
    }
};
#endif

class COutputSerializer;
class CInputSerializer;

/// <summary>
/// 可序列化接口
/// </summary>
struct ISerializable
{
    /// <summary>
    /// 用于标记一个class的唯一ID
    /// </summary>
    /// <returns></returns>
    virtual uint32_t ClassId() const = 0;

    /// <summary>
    /// 向序列化组件中写入内容
    /// </summary>
    /// <param name="ser">序列化组件引用</param>
    /// <returns>写入的字节数</returns>
    virtual size_t WriteTo(COutputSerializer& ser) const = 0;

    /// <summary>
    /// 从序列化组件中读取内容
    /// </summary>
    /// <param name="ser">序列化组件引用</param>
    /// <returns>读取的字节数</returns>
    virtual size_t ReadFrom(CInputSerializer& ser) = 0;
};

/// <summary>
/// 用于输出到文件的序列化组件
/// </summary>
class COutputSerializer
{
private:
    mutable std::ofstream m_stream;
    std::map<const ISerializable*, uint32_t> m_objectMap;
    EndianConverter m_endianConverter;
    uint32_t m_objectId;
private:
    COutputSerializer(const COutputSerializer&) = delete;
    COutputSerializer(COutputSerializer&&) = delete;
    COutputSerializer& operator=(const COutputSerializer&) = delete;
    COutputSerializer& operator=(COutputSerializer&&) = delete;
public:
    /// <summary>
    /// 构造一个实例
    /// </summary>
    /// <param name="fileName">文件名</param>
    /// <param name="endian">输出数字时使用的字节序</param>
    COutputSerializer(const std::string& fileName, std::endian endian)
        :m_endianConverter(endian), m_objectId(1000)
    {
        m_stream.open(fileName, std::ios::out | std::ios::binary);
        auto end = EndianConverter::ToInt8(endian);
        m_stream.write(reinterpret_cast<const char*>(&end), 1); //首先写入字节序
    }

    /// <summary>
    /// 构造一个实例
    /// </summary>
    /// <param name="fileName">文件名</param>
    /// <param name="endian">输出数字时使用的字节序</param>
    COutputSerializer(const std::wstring& fileName, std::endian endian)
        :m_endianConverter(endian), m_objectId(1000)
    {
#ifdef __GNUC__
        m_stream.open(StringConvert::ws2s(fileName), std::ios::out | std::ios::binary);
#else
        m_stream.open(fileName, std::ios::out | std::ios::binary);
#endif
        auto end = EndianConverter::ToInt8(endian);
        m_stream.write(reinterpret_cast<const char*>(&end), 1); //首先写入字节序
    }

    virtual ~COutputSerializer() {}

    /// <summary>
    /// 当前文件写指针位置
    /// </summary>
    /// <returns></returns>
    size_t Position() const { return m_stream.tellp(); }

    /// <summary>
    /// 写入若干字节
    /// </summary>
    /// <param name="bytes">字节头部</param>
    /// <param name="count">待写的数量</param>
    /// <returns></returns>
    COutputSerializer& Write(const void* bytes, size_t count)
    {
        m_stream.write(static_cast<const char*>(bytes), count);
        return *this;
    }

    COutputSerializer& operator << (uint8_t value)
    {
        m_stream.write(reinterpret_cast<const char*>(&value), sizeof(value));
        return *this;
    }

    COutputSerializer& operator << (bool value)
    {
        uint8_t v = value ? 1 : 0;
        return operator<<(v);
    }

    COutputSerializer& operator << (char value)
    {
        m_stream.write(&value, sizeof(value));
        return *this;
    }

    COutputSerializer& operator << (int16_t value)
    {
        value = m_endianConverter.EndianConverted(value);
        m_stream.write(reinterpret_cast<const char*>(&value), sizeof(value));
        return *this;
    }

    COutputSerializer& operator << (uint16_t value)
    {
        value = m_endianConverter.EndianConverted(value);
        m_stream.write(reinterpret_cast<const char*>(&value), sizeof(value));
        return *this;
    }

    COutputSerializer& operator << (int32_t value)
    {
        value = m_endianConverter.EndianConverted(value);
        m_stream.write(reinterpret_cast<const char*>(&value), sizeof(value));
        return *this;
    }

    COutputSerializer& operator << (uint32_t value)
    {
        value = m_endianConverter.EndianConverted(value);
        m_stream.write(reinterpret_cast<const char*>(&value), sizeof(value));
        return *this;
    }

    COutputSerializer& operator << (int64_t value)
    {
        value = m_endianConverter.EndianConverted(value);
        m_stream.write(reinterpret_cast<const char*>(&value), sizeof(value));
        return *this;
    }

    COutputSerializer& operator << (uint64_t value)
    {
        value = m_endianConverter.EndianConverted(value);
        m_stream.write(reinterpret_cast<const char*>(&value), sizeof(value));
        return *this;
    }

    COutputSerializer& operator << (float value)
    {
        value = m_endianConverter.EndianConverted(value);
        m_stream.write(reinterpret_cast<const char*>(&value), sizeof(value));
        return *this;
    }

    COutputSerializer& operator << (double value)
    {
        value = m_endianConverter.EndianConverted(value);
        m_stream.write(reinterpret_cast<const char*>(&value), sizeof(value));
        return *this;
    }

    COutputSerializer& operator << (const std::string& value)
    {
        *this << static_cast<uint32_t>(value.size());
        m_stream.write(value.c_str(), value.size());
        return *this;
    }

    /// <summary>
    /// 写入一个ISerializable对象
    /// </summary>
    /// <param name="object">待写入的对象</param>
    /// <returns></returns>
    COutputSerializer& operator << (const ISerializable& object)
    {
        return *this << &object;
    }

    /// <summary>
    /// 写入一个可空的ISerializable对象
    /// </summary>
    /// <param name="object">待写入的对象指针，可以为空</param>
    /// <returns></returns>
    COutputSerializer& operator << (const ISerializable* object)
    {
        WriteObject(object);
        return *this;
    }

    COutputSerializer& operator << (const std::shared_ptr<ISerializable>& object)
    {
        return operator << (object.get());
    }

    /// <summary>
    /// 写入一个可空的ISerializable对象
    /// </summary>
    /// <param name="object">待写入的对象指针，可以为空</param>
    void WriteObject(const ISerializable* object)
    {
        if (!object)
        {
            *this << uint32_t{ 0 }; //空指针id为0
            return;
        }
        auto iter = m_objectMap.find(object);
        if (iter != m_objectMap.end()) // 已经存在写入的拷贝, 只记录其ID
        {
            auto objectId = iter->second;
            *this << objectId;
        }
        else
        {
            auto objectId = ++m_objectId;
            m_objectMap[object] = objectId;

            uint32_t length = 0; // 数据长度占位符号
            *this << objectId << object->ClassId() << length;
            auto begin = Position();
            auto bytes = object->WriteTo(*this);
            auto end = Position();
            // 回写数据长度
            length = (uint32_t)(end - begin);
            assert(length == bytes); //object->WriteTo的实现有bug才会出现断言失败
            begin -= sizeof(length);
            m_stream.seekp(begin, std::ios::beg);
            *this << length;

            // 恢复文件指针
            m_stream.seekp(end, std::ios::beg);
        }
    }

    /// <summary>
    /// 写入一个 std::vector<std::shared_ptr<T>>
    /// </summary>
    /// <typeparam name="T">
    /// 模板参数类型
    /// </typeparam>
    /// <param name="object">待写入的vector对象</param>
    /// <returns></returns>
    template<class T>
    COutputSerializer& operator << (const std::vector<std::shared_ptr<T>>& object)
    {
        uint32_t count = static_cast<uint32_t>(object.size());
        *this << count;
        for (auto& ptr : object)
        {
            *this << ptr.get(); // shared_ptr<T> 必须能协变成 shared_ptr<ISerializable>
        }
        return *this;
    }
    /// <summary>
    /// 写入一个非 std::vector<std::shared_ptr<T>> 的vector
    /// </summary>
    /// <typeparam name="T">
    /// 参数类型，非 std::shared_ptr<T>
    /// </typeparam>
    /// <param name="object">待写入的vector对象</param>
    /// <returns></returns>
    template<typename T>
    COutputSerializer& operator << (const std::vector<T>& object)
    {
        uint32_t count = static_cast<uint32_t>(object.size());
        *this << count;
        for (auto& obj : object)
        {
            *this << obj; // T 需要支持 operator<<
        }
        return *this;
    }
};

/// <summary>
/// 带头部的序列化。文件头部可以存放一些文件总体信息的meta数据，如版本号、版权信息等
/// </summary>
/// <typeparam name="THeader">
/// 头部信息描述对象，需要定义 COutputSerializer& operator << (COutputSerializer&, const THeader&)
/// </typeparam>
template<typename THeader>
class COHSerializer : public COutputSerializer
{
private:
    const THeader& m_header;
public:
    /// <summary>
    /// 构造一个实例，采用本机字节序来存储数字
    /// </summary>
    /// <param name="fileName">文件名</param>
    /// <param name="header">头部</param>
    COHSerializer(const std::string& fileName, const THeader& header)
        :COutputSerializer(fileName, std::endian::native), m_header(header)
    {
        *this << m_header;
    }

    COHSerializer(const std::wstring& fileName, const THeader& header)
        :COutputSerializer(fileName, std::endian::big), m_header(header)
    {
        *this << m_header;
    }

    const THeader& Header() const { return m_header; }
};

/// <summary>
/// 序列化对象工厂，用于创建ISerializable对象
/// </summary>
class CSerialiableFactory
{
public:
    /// <summary>
    /// 工厂方法，由各个实现了ISerializable接口的具体类来定义
    /// </summary>
    typedef std::shared_ptr<ISerializable>(*Creator)();
private:
    CSerialiableFactory() {}
    CSerialiableFactory(const CSerialiableFactory&) = delete;
    CSerialiableFactory& operator=(const CSerialiableFactory&) = delete;
    CSerialiableFactory(CSerialiableFactory&&) = delete;
    CSerialiableFactory& operator=(CSerialiableFactory&&) = delete;
public:
    /// <summary>
    /// 序列化对象工厂的唯一实例
    /// </summary>
    /// <returns></returns>
    static CSerialiableFactory& Instance()
    {
        static CSerialiableFactory instance;
        return instance;
    }

    /// <summary>
    /// 注册一个工厂方法
    /// </summary>
    /// <param name="classId">实现了ISerializable接口的类的ClassId</param>
    /// <param name="factoryMethod">创建该类实例的工厂方法</param>
    void Register(uint32_t classId, Creator factoryMethod)
    {
        m_factories[classId] = factoryMethod;
    }

    /// <summary>
    /// 根据 ClassId创建一个ISerializable的派生类实例
    /// </summary>
    /// <param name="classId"></param>
    /// <returns></returns>
    std::shared_ptr<ISerializable> Create(uint32_t classId) const
    {
        auto iter = m_factories.find(classId);
        assert(iter != m_factories.end());
        if (iter != m_factories.end())
        {
            auto creator = iter->second;
            return creator();
        }
        return nullptr;
    }

    size_t CreatorCount() const { return m_factories.size(); }
private:
    std::map<uint32_t, Creator> m_factories;
};


// 定义一个宏来提供默认的工厂注册实现
// 采用一个独立的 CLS_TAG 参数，可避免类名重命名时对ClassID造成变化
#define IMPL_SERIAL(CLS_NAME, CLS_TAG) \
static _SerialiableFactoryRegister<CLS_NAME> factory##CLS_NAME(CLS_TAG); \
uint32_t CLS_NAME::ClassId() const { return factory##CLS_NAME.ClassId(); } \
std::shared_ptr<ISerializable> CLS_NAME::NewInstance() { return std::shared_ptr<ISerializable>(new CLS_NAME()); }

// 定义一个宏来声明工厂注册方法
#define DECL_SERIAL() \
public: static std::shared_ptr<ISerializable> NewInstance(); \
public: virtual uint32_t ClassId() const override;


/// <summary>
/// 工厂方法注册器，利用静态变量的自动初始化来执行注册动作
/// </summary>
/// <typeparam name="T"></typeparam>
template<class T>
class _SerialiableFactoryRegister
{
    //注意：classId是通过字符串hash得到，所以改变字符串将会导致文件中的对象无法读取
    uint32_t m_classId;
public:
    _SerialiableFactoryRegister(const char* classTag)
        : m_classId(Hash(classTag))
    {
        CSerialiableFactory::Instance().Register(m_classId, T::NewInstance);
    }
    uint32_t ClassId() const { return m_classId; }
private:
    static uint32_t Hash(const char* classTag)
    {
        // FNV1a32 算法计算
        uint32_t constexpr fnv_prime = 16777619U;
        uint32_t constexpr fnv_offset_basis = 2166136261U;

        uint32_t hash = fnv_offset_basis;

        for (; *classTag; ++classTag) {
            hash ^= *classTag;
            hash *= fnv_prime;
        }

        return hash;
    }
};

/// <summary>
/// 用于从文件输入的序列化组件
/// </summary>
class CInputSerializer
{
private:
    mutable std::ifstream m_stream;
    std::map<uint32_t, std::shared_ptr<ISerializable>> m_objectMap;
    EndianConverter m_endianConverter;
public:
    /// <summary>
    /// 构造一个实例
    /// </summary>
    /// <param name="fileName">文件名</param>
    CInputSerializer(const std::string& fileName)
        :m_endianConverter(std::endian::native)
    {
        m_stream.open(fileName, std::ios::in | std::ios::binary);
        Prepare();
    }

    /// <summary>
    /// 构造一个实例
    /// </summary>
    /// <param name="fileName">文件名</param>
    CInputSerializer(const std::wstring& fileName)
        :m_endianConverter(std::endian::native)
    {
#ifdef __GNUC__
        m_stream.open(StringConvert::ws2s(fileName), std::ios::in | std::ios::binary);
#else
        m_stream.open(fileName, std::ios::in | std::ios::binary);
#endif
        Prepare();
    }

    virtual ~CInputSerializer() {}
private:
    void Prepare()
    {
        uint8_t endian = 0;
        m_stream.read(reinterpret_cast<char*>(&endian), 1); //首先读入字节序
        auto end = EndianConverter::ToEndian(endian);
        m_endianConverter = EndianConverter(end);
    }
public:
    /// <summary>
    /// 当前文件读指针位置
    /// </summary>
    /// <returns></returns>
    size_t Position() const { return m_stream.tellg(); }

    /// <summary>
    /// 读取若干字节
    /// </summary>
    /// <param name="buffer">存放读取字节的缓冲区</param>
    /// <param name="count">读取的字节数量</param>
    /// <returns></returns>
    CInputSerializer& Read(void* buffer, size_t count)
    {
        m_stream.read(reinterpret_cast<char*>(buffer), count);
        return *this;
    }

    CInputSerializer& operator>>(uint8_t& value)
    {
        m_stream.read(reinterpret_cast<char*>(&value), sizeof(value));
        return *this;
    }

    CInputSerializer& operator>>(bool& value)
    {
        uint8_t v;
        operator>>(v);
        value = v == 1;
        return *this;
    }

    CInputSerializer& operator>>(char& value)
    {
        m_stream.read(&value, sizeof(value));
        return *this;
    }

    CInputSerializer& operator>>(int16_t& value)
    {
        m_stream.read(reinterpret_cast<char*>(&value), sizeof(value));
        value = m_endianConverter.EndianConverted(value);
        return *this;
    }

    CInputSerializer& operator>>(uint16_t& value)
    {
        m_stream.read(reinterpret_cast<char*>(&value), sizeof(value));
        value = m_endianConverter.EndianConverted(value);
        return *this;
    }

    CInputSerializer& operator>>(int32_t& value)
    {
        m_stream.read(reinterpret_cast<char*>(&value), sizeof(value));
        value = m_endianConverter.EndianConverted(value);
        return *this;
    }

    CInputSerializer& operator>>(uint32_t& value)
    {
        m_stream.read(reinterpret_cast<char*>(&value), sizeof(value));
        value = m_endianConverter.EndianConverted(value);
        return *this;
    }

    CInputSerializer& operator>>(int64_t& value)
    {
        m_stream.read(reinterpret_cast<char*>(&value), sizeof(value));
        value = m_endianConverter.EndianConverted(value);
        return *this;
    }

    CInputSerializer& operator>>(uint64_t& value)
    {
        m_stream.read(reinterpret_cast<char*>(&value), sizeof(value));
        value = m_endianConverter.EndianConverted(value);
        return *this;
    }

    CInputSerializer& operator>>(float& value)
    {
        m_stream.read(reinterpret_cast<char*>(&value), sizeof(value));
        value = m_endianConverter.EndianConverted(value);
        return *this;
    }

    CInputSerializer& operator>>(double& value)
    {
        m_stream.read(reinterpret_cast<char*>(&value), sizeof(value));
        value = m_endianConverter.EndianConverted(value);
        return *this;
    }

    CInputSerializer& operator>>(std::string& value)
    {
        uint32_t count = 0;
        *this >> count;
        value.clear();
        value.reserve(static_cast<size_t>(count + 1));
        value.resize(count, 0);
        m_stream.read(value.data(), count);
        return *this;
    }

    /// <summary>
    /// 读取一个ISerializable的派生对象
    /// </summary>
    /// <typeparam name="T">
    /// 模板参数，需要实现 ISerializable
    /// </typeparam>
    /// <param name="value">读取到的对象</param>
    /// <returns></returns>
    template<class T>
    CInputSerializer& operator>>(std::shared_ptr<T>& value)
    {
        value = std::dynamic_pointer_cast<T>(ReadObject());
        return *this;
    }

    template<class T>
    CInputSerializer& operator>>(T*& value)
    {
        value = std::dynamic_pointer_cast<T>(ReadObject()).get();
        return *this;
    }

    /// <summary>
    /// 读取一个ISerializable的派生对象
    /// </summary>
    std::shared_ptr<ISerializable> ReadObject()
    {
        uint32_t objectId = 0;
        *this >> objectId;
        if (objectId == 0)
        {
            return nullptr;
        }
        auto iter = m_objectMap.find(objectId);
        if (iter != m_objectMap.end()) // 已经读取过
        {
            return iter->second;
        }

        uint32_t classId;
        uint32_t length;
        *this >> classId >> length;

        auto value = CSerialiableFactory::Instance().Create(classId);
        m_objectMap[objectId] = value;
        if (value)
        {
            auto bytes = value->ReadFrom(*this);
            assert(bytes == length); //object.ReadFrom的实现有bug才会出现断言失败
        }
        else
        {
            m_stream.seekg(length, std::ios::cur); // 跳过不能识别classId的对象
        }
        return value;
    }

    /// <summary>
    /// 读取一个非std::vector<std::shared_ptr<T>>的vector
    /// </summary>
    /// <typeparam name="T">
    /// 模板参数，需要实现 operator >>，支持默认构造及移动构造
    /// </typeparam>
    /// <param name="value">存放读取的结果的容器，读取时不会清空原来的内容</param>
    /// <returns></returns>
    template<class T>
    CInputSerializer& operator>>(std::vector<T>& value)
    {
        uint32_t count;
        *this >> count;
        value.reserve(value.size() + count);
        for (uint32_t i = 0; i < count; ++i)
        {
            T item;
            *this >> item;
            value.push_back(std::move(item));
        }
        return *this;
    }
};

/// <summary>
/// 带头部的序列化。文件头部可以存放一些文件总体信息的meta数据，如版本号、版权信息等
/// </summary>
/// <typeparam name="THeader">
/// 头部信息的描述。需要有默认构造函数且实现 CInputSerializer& operator >> (CInputSerializer&, THeader&)
/// </typeparam>
template<typename THeader>
class CIHSerializer : public CInputSerializer
{
private:
    THeader& m_header;
public:
    CIHSerializer(const std::string& fileName, THeader& header)
        :CInputSerializer(fileName), m_header(header)
    {
        *this >> m_header;
    }

    CIHSerializer(const std::wstring& fileName, THeader& header)
        :CInputSerializer(fileName), m_header(header)
    {
        *this >> m_header;
    }

    const THeader& Header() const { return m_header; }
};